<!DOCTYPE html>
<html>
<head>
<title>DAY13</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
/* Source: https://github.com/nicolahery/markdownpad-github */

/* RESET
=============================================================================*/

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
}

/* BODY
=============================================================================*/

body {
  font-family: Helvetica, arial, freesans, clean, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  color: #333;
  background-color: #fff;
  padding: 20px;
  max-width: 960px;
  margin: 0 auto;
}

body>*:first-child {
  margin-top: 0 !important;
}

body>*:last-child {
  margin-bottom: 0 !important;
}

/* BLOCKS
=============================================================================*/

p, blockquote, ul, ol, dl, table, pre {
  margin: 15px 0;
}

/* HEADERS
=============================================================================*/

h1, h2, h3, h4, h5, h6 {
  margin: 20px 0 10px;
  padding: 0;
  font-weight: bold;
  -webkit-font-smoothing: antialiased;
}

h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
  font-size: inherit;
}

h1 {
  font-size: 28px;
  color: #000;
}

h2 {
  font-size: 24px;
  border-bottom: 1px solid #ccc;
  color: #000;
}

h3 {
  font-size: 18px;
}

h4 {
  font-size: 16px;
}

h5 {
  font-size: 14px;
}

h6 {
  color: #777;
  font-size: 14px;
}

body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
  margin-top: 0;
  padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
  margin-top: 0;
  padding-top: 0;
}

h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
  margin-top: 10px;
}

/* LINKS
=============================================================================*/

a {
  color: #4183C4;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* LISTS
=============================================================================*/

ul, ol {
  padding-left: 30px;
}

ul li > :first-child, 
ol li > :first-child, 
ul li ul:first-of-type, 
ol li ol:first-of-type, 
ul li ol:first-of-type, 
ol li ul:first-of-type {
  margin-top: 0px;
}

ul ul, ul ol, ol ol, ol ul {
  margin-bottom: 0;
}

dl {
  padding: 0;
}

dl dt {
  font-size: 14px;
  font-weight: bold;
  font-style: italic;
  padding: 0;
  margin: 15px 0 5px;
}

dl dt:first-child {
  padding: 0;
}

dl dt>:first-child {
  margin-top: 0px;
}

dl dt>:last-child {
  margin-bottom: 0px;
}

dl dd {
  margin: 0 0 15px;
  padding: 0 15px;
}

dl dd>:first-child {
  margin-top: 0px;
}

dl dd>:last-child {
  margin-bottom: 0px;
}

/* CODE
=============================================================================*/

pre, code, tt {
  font-size: 12px;
  font-family: Consolas, "Liberation Mono", Courier, monospace;
}

code, tt {
  margin: 0 0px;
  padding: 0px 0px;
  white-space: nowrap;
  border: 1px solid #eaeaea;
  background-color: #f8f8f8;
  border-radius: 3px;
}

pre>code {
  margin: 0;
  padding: 0;
  white-space: pre;
  border: none;
  background: transparent;
}

pre {
  background-color: #f8f8f8;
  border: 1px solid #ccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px;
}

pre code, pre tt {
  background-color: transparent;
  border: none;
}

kbd {
    -moz-border-bottom-colors: none;
    -moz-border-left-colors: none;
    -moz-border-right-colors: none;
    -moz-border-top-colors: none;
    background-color: #DDDDDD;
    background-image: linear-gradient(#F1F1F1, #DDDDDD);
    background-repeat: repeat-x;
    border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
    border-image: none;
    border-radius: 2px 2px 2px 2px;
    border-style: solid;
    border-width: 1px;
    font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
    line-height: 10px;
    padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
  border-left: 4px solid #DDD;
  padding: 0 15px;
  color: #777;
}

blockquote>:first-child {
  margin-top: 0px;
}

blockquote>:last-child {
  margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
  clear: both;
  margin: 15px 0;
  height: 0px;
  overflow: hidden;
  border: none;
  background: transparent;
  border-bottom: 4px solid #ddd;
  padding: 0;
}

/* TABLES
=============================================================================*/

table th {
  font-weight: bold;
}

table th, table td {
  border: 1px solid #ccc;
  padding: 6px 13px;
}

table tr {
  border-top: 1px solid #ccc;
  background-color: #fff;
}

table tr:nth-child(2n) {
  background-color: #f8f8f8;
}

/* IMAGES
=============================================================================*/

img {
  max-width: 100%
}
</style>
</head>
<body>
<h3>创建订单-创建数据表</h3>
<p>订单相关数据必须使用2张表才可以存储，所以，需要先创建订单表：</p>
<pre><code>CREATE TABLE t_order (
    oid INT AUTO_INCREMENT COMMENT '订单id',
    uid INT COMMENT '用户id',
    recv_name VARCHAR(50) COMMENT '收货人姓名',
    recv_phone VARCHAR(20) COMMENT '收货人电话',
    recv_address VARCHAR(250) COMMENT '收货人地址',
    total_price BIGINT(20) COMMENT '总价',
    state INT COMMENT '状态：0-未支付，1-已支付，2-已取消',
    order_time DATETIME COMMENT '下单时间',
    pay_time DATETIME COMMENT '支付时间',
    created_user VARCHAR(20) COMMENT '创建人',
    created_time DATETIME COMMENT '创建时间',
    modified_user VARCHAR(20) COMMENT '最后修改人',
    modified_time DATETIME COMMENT '最后修改时间',
    PRIMARY KEY (oid)
) DEFAULT CHARSET=UTF8;
</code></pre>

<p>然后，还要创建订单商品表：</p>
<pre><code>CREATE TABLE t_order_item (
    id INT AUTO_INCREMENT COMMENT 'id',
    oid INT COMMENT '订单id',
    gid BIGINT COMMENT '商品id',
    title VARCHAR(100) COMMENT '商品标题',
    image VARCHAR(500) COMMENT '商品图片',
    price BIGINT COMMENT '商品单价',
    num INT COMMENT '购买数量',
    created_user VARCHAR(20) COMMENT '创建人',
    created_time DATETIME COMMENT '创建时间',
    modified_user VARCHAR(20) COMMENT '最后修改人',
    modified_time DATETIME COMMENT '最后修改时间',
    PRIMARY KEY (id)
) DEFAULT CHARSET=UTF8;
</code></pre>

<h3>创建订单-创建实体类</h3>
<p>创建<code>cn.tedu.store.entity.Order</code>实体类：</p>
<pre><code>public class Order implements Serializable {

    private static final long serialVersionUID = -7634515703569972463L;

    private Integer oid;
    private Integer uid;
    private String recvName;
    private String recvPhone;
    private String recvAddress;
    private Long totalPrice;
    private Integer state;
    private Date orderTime;
    private Date payTime;

}
</code></pre>

<p>创建<code>cn.tedu.store.entity.OrderItem</code>实体类：</p>
<pre><code>public class OrderItem extends BaseEntity {

    private static final long serialVersionUID = 58159245395814558L;

    private Integer id;
    private Integer oid;
    private Long gid;
    private String title;
    private String image;
    private Long price;
    private Integer num;

}
</code></pre>

<h3>创建订单-持久层</h3>
<p>创建<code>cn.tedu.store.mapper.OrderMapper</code>接口，并添加抽象方法：</p>
<pre><code>Integer insertOrder(Order order);

Integer insertOrderItem(OrderItem orderItem);
</code></pre>

<p>复制得到<code>OrderMapper.xml</code>，修改根节点的<code>namespace</code>属性对应以上接口，并配置以上2个抽象方法的映射：</p>
<pre><code>&lt;mapper namespace=&quot;cn.tedu.store.mapper.OrderMapper&quot;&gt;

    &lt;!-- 插入订单数据 --&gt;
    &lt;!-- Integer insertOrder(Order order) --&gt;
    &lt;insert id=&quot;insertOrder&quot;
        useGeneratedKeys=&quot;true&quot;
        keyProperty=&quot;oid&quot;&gt;
        INSERT INTO t_order (
            uid, recv_name,
            recv_phone, recv_address,
            total_price, state,
            order_time, pay_time,
            created_user, created_time,
            modified_user, modified_time
        ) VALUES (
            #{uid}, #{recvName},
            #{recvPhone}, #{recvAddress},
            #{totalPrice}, #{state},
            #{orderTime}, #{payTime},
            #{createdUser}, #{createdTime},
            #{modifiedUser}, #{modifiedTime}
        )
    &lt;/insert&gt;

    &lt;!-- 插入订单商品数据 --&gt;
    &lt;!-- Integer insertOrderItem(OrderItem orderItem) --&gt;
    &lt;insert id=&quot;insertOrderItem&quot;
        useGeneratedKeys=&quot;true&quot;
        keyProperty=&quot;id&quot;&gt;
        INSERT INTO t_order_item (
            oid, gid,
            title, image,
            price, num,
            created_user, created_time,
            modified_user, modified_time
        ) VALUES (
            #{oid}, #{gid},
            #{title}, #{image},
            #{price}, #{num},
            #{createdUser}, #{createdTime},
            #{modifiedUser}, #{modifiedTime}
        )
    &lt;/insert&gt;

&lt;/mapper&gt;
</code></pre>

<p>在<strong>src/test/java</strong>下创建<code>cn.tedu.store.mapper.OrderMapperTests</code>测试类，编写并执行测试方法：</p>
<pre><code>@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderMapperTests {

    @Autowired
    private OrderMapper mapper;

    @Test
    public void insertOrder() {
        Order order = new Order();
        order.setUid(1);
        Integer rows = mapper.insertOrder(order);
        System.err.println(&quot;rows=&quot; + rows);
    }

    @Test
    public void insertOrderItem() {
        OrderItem orderItem = new OrderItem();
        orderItem.setOid(1);
        Integer rows = mapper.insertOrderItem(orderItem);
        System.err.println(&quot;rows=&quot; + rows);
    }

}
</code></pre>

<h3>创建订单-业务层</h3>
<p>此次涉及的都是新增数据操作，新增之前几乎没有需要验证的规则，可能抛出的异常主要还是新增数据时的<code>InsertException</code>。</p>
<p>在处理业务过程中，需要“根据收货地址aid查询收货地址详情”，目前，在<code>AddressMapper</code>中已经定义了<code>findByAid()</code>，但是查询的字段列表并不符合要求，需要先补充查询所需的字段：</p>
<pre><code>&lt;!-- 根据收货地址的数据id查询详情 --&gt;
&lt;!-- Address findByAid(Integer aid) --&gt;
&lt;select id=&quot;findByAid&quot;
    resultType=&quot;cn.tedu.store.entity.Address&quot;&gt;
    SELECT
        uid, is_default AS isDefault,
        name, phone,
        province_name AS provinceName,
        city_name AS cityName,
        area_name AS areaName,
        address
    FROM 
        t_address
    WHERE 
        aid=#{aid}
&lt;/select&gt;
</code></pre>

<p>然后，还需要在<code>IAddressService</code>中添加<code>getByAid()</code>方法：</p>
<pre><code>/**
 * 根据收货地址的数据id查询详情
 * @param aid 收货地址的数据id
 * @return 匹配的收货地址的详情，如果没有匹配的数据，则返回null
 */
Address getByAid(Integer aid);
</code></pre>

<p>并在<code>AddressServiceImpl</code>中实现以上方法，由于此前已经私有化实现了<code>findByAid()</code>方法，所以，此次实现时直接调用即可：</p>
<pre><code>public Address getByAid(Integer aid) {
    return findByAid(aid);
}
</code></pre>

<p>先创建<code>cn.tedu.store.service.IOrderService</code>业务层接口，并添加抽象方法：</p>
<pre><code>Order create(Integer aid, Integer[] cids, Integer uid, String username) throws InsertException;
</code></pre>

<p>然后创建<code>cn.tedu.store.service.OrderServiceImpl</code>业务层实现类，实现以上接口，添加<code>@Service</code>注解，在类中声明<code>@Autowired private OrderMapper orderMapper;</code>持久层对象，并声明<code>@Autowired private IAddressService addressService;</code>和<code>@Autowired private ICartService cartService;</code>：</p>
<pre><code>@Service
public class OrderServiceImpl implements IOrderService {

    @Autowired private OrderMapper orderMapper;
    @Autowired private IAddressService addressService;
    @Autowired private ICartService cartService;

}
</code></pre>

<p>先私有化实现持久层中的方法：</p>
<pre><code>/**
 * 插入订单数据
 * @param order 订单数据
 * @throws InsertException 插入数据异常
 */
private void insertOrder(Order order) throws InsertException {
    Integer rows = orderMapper.insertOrder(order);
    if (rows != 1) {
        throw new InsertException(
            &quot;创建订单失败！插入订单数据时出现未知错误！&quot;);
    }
}

/**
 * 插入订单商品数据
 * @param orderItem 订单商品数据
 * @throws InsertException 插入数据异常
 */
private void insertOrderItem(OrderItem orderItem) throws InsertException {
    Integer rows = orderMapper.insertOrderItem(orderItem);
    if (rows != 1) {
        throw new InsertException(
            &quot;创建订单失败！插入订单商品数据时出现未知错误！&quot;);
    }
}
</code></pre>

<p>然后再重写接口中定义的抽象方法：</p>
<pre><code>@Transactional
public Order create(Integer aid, Integer[] cids, Integer uid, String username) throws InsertException {
    // 创建当前时间对象：now

    // 根据参数cids查询对应的购物车数据，得到List&lt;CartVO&gt;对象
    // 遍历以上查询到的对象，根据各元素的price和num计算得到总价

    // 根据收货地址aid查询收货地址详情

    // 创建order对象：Order order = new Order();
    // 封装order对象中的属性值：order.setUid(uid);
    // 封装order对象中的属性值：recv_name,recv_phone,recv_address
    // 封装order对象中的属性值：total_price
    // 封装order对象中的属性值：state(0)
    // 封装order对象中的属性值：order_time(now)
    // 封装order对象中的属性值：pay_time(null)
    // 封装order对象中的属性值：日志
    // 插入订单数据：insertOrder(order)

    // 遍历以上查询得到的List&lt;CartVO&gt;对象
    // -- 创建orderItem对象：OrderItem ordeItemr = new OrderItem();
    // -- 封装orderItem对象中的属性值：oid
    // -- 封装orderItem对象中的属性值：gid,price,title,image,num
    // -- 封装orderItem对象中的属性值：日志
    // -- 插入订单商品数据：insertOrderItem(itemItem)
}
</code></pre>

<p>具体实现为：</p>
<pre><code>@Override
@Transactional
public Order create(Integer aid, 
    Integer[] cids, Integer uid, String username) throws InsertException {
    // 创建当前时间对象：now
    Date now = new Date();

    // 根据参数cids查询对应的购物车数据，得到List&lt;CartVO&gt;对象
    List&lt;CartVO&gt; carts = cartService.getByCids(cids, uid);
    // 遍历以上查询到的对象，根据各元素的price和num计算得到总价
    Long totalPrice = 0L;
    for (CartVO cartVO : carts) {
        totalPrice += cartVO.getPrice() * cartVO.getNum();
    }

    // 根据收货地址aid查询收货地址详情
    Address address = addressService.getByAid(aid);

    // 创建order对象：Order order = new Order();
    Order order = new Order();
    // 封装order对象中的属性值：order.setUid(uid);
    order.setUid(uid);
    // 封装order对象中的属性值：recv_name,recv_phone,recv_address
    order.setRecvName(address.getName());
    order.setRecvPhone(address.getPhone());
    order.setRecvAddress(address.getProvinceName() + address.getCityName() + address.getAreaName() + address.getAddress());
    // 封装order对象中的属性值：total_price
    order.setTotalPrice(totalPrice);
    // 封装order对象中的属性值：state(0)
    order.setState(0);
    // 封装order对象中的属性值：order_time(now)
    order.setOrderTime(now);
    // 封装order对象中的属性值：pay_time(null)
    order.setPayTime(null);
    // 封装order对象中的属性值：日志
    order.setCreatedUser(username);
    order.setCreatedTime(now);
    order.setModifiedUser(username);
    order.setModifiedTime(now);
    // 插入订单数据：insertOrder(order)
    insertOrder(order);

    // 遍历以上查询得到的List&lt;CartVO&gt;对象
    for (CartVO cartVO : carts) {
        // 创建orderItem对象：OrderItem ordeItemr = new OrderItem();
        OrderItem orderItem = new OrderItem();
        // 封装orderItem对象中的属性值：oid
        orderItem.setOid(order.getOid());
        // 封装orderItem对象中的属性值：gid,price,title,image,num
        orderItem.setGid(cartVO.getGid());
        orderItem.setPrice(cartVO.getPrice());
        orderItem.setTitle(cartVO.getTitle());
        orderItem.setImage(cartVO.getImage());
        orderItem.setNum(cartVO.getNum());
        // 封装orderItem对象中的属性值：日志
        orderItem.setCreatedUser(username);
        orderItem.setCreatedTime(now);
        orderItem.setModifiedUser(username);
        orderItem.setModifiedTime(now);
        // 插入订单商品数据：insertOrderItem(itemItem)
        insertOrderItem(orderItem);
    }

    // TODO 删除购物车中对应的数据
    // TODO 修改对应的商品的库存量

    // 返回
    return order;
}
</code></pre>

<p>最后，编写并执行单元测试：</p>
<pre><code>@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderServiceTests {

    @Autowired
    private IOrderService service;

    @Test
    public void create() {
        Integer aid = 20;
        Integer[] cids = {7,9,12,13};
        Integer uid = 7;
        String username = &quot;超级管理员&quot;;
        Order order = service.create(aid, cids, uid, username);
        System.err.println(order);
    }

}
</code></pre>

<h3>创建订单-控制器</h3>
<p>创建<code>cn.tedu.store.controller.OrderController</code>控制器类，继承自<code>BaseController</code>，添加<code>@RestController</code>和<code>@RequestMapping(&quot;orders&quot;)</code>注解，在类中声明<code>@Autowired private IOrderService orderService;</code>业务层对象：</p>
<pre><code>@RestController
@RequestMapping(&quot;orders&quot;)
public class OrderController extends BaseController {

    @Autowired 
    private IOrderService orderService;

}
</code></pre>

<p>然后，在类中添加处理请求的方法：</p>
<pre><code>@RequestMapping(&quot;create&quot;)
public JsonResult&lt;Order&gt; create(
    Integer aid, Integer[] cids, HttpSession session) {
    // 从Session中获取uid和username
    Integer uid = getUidFromSession(session);
    String username = getUsernameFromSession(session);
    // 调用业务层对象执行
    Order data = orderService.create(aid, cids, uid, username);
    // 响应
    return new JsonResult&lt;&gt;(SUCCESS, data);
}
</code></pre>

<p>完成后，可以先登录，然后通过<code>http://localhost:8080/orders/create?aid=20&amp;cids=9&amp;cids=10&amp;cids=12&amp;cids=13</code>进行测试。</p>
<h3>Spring AOP</h3>
<p>AOP指的是“面向切面的编程”。</p>
<p>AOP并不是Spring框架的特征，只不过Spring提供了很好的支持，使得AOP更易于实现。</p>
<p>在使用Spring AOP之前，需要添加AOP相关的依赖：</p>
<pre><code>&lt;dependency&gt;
  &lt;groupId&gt;aspectj&lt;/groupId&gt;
  &lt;artifactId&gt;aspectjweaver&lt;/artifactId&gt;
  &lt;version&gt;1.5.4&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
  &lt;groupId&gt;aspectj&lt;/groupId&gt;
  &lt;artifactId&gt;aspectjtools&lt;/artifactId&gt;
  &lt;version&gt;1.5.4&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>

<p>接下来，需要创建<code>cn.tedu.store.aop.TimeElapsedAspect</code>切面类，该切面类的对象将由Spring框架进行管理，所以，必须在<code>cn.tedu.store</code>包下，并且，需要添加<code>@Component</code>和<code>@Aspect</code>注解：</p>
<pre><code>@Component
@Aspect
public class TimeElapsedAspect {

}
</code></pre>

<p>然后，在类中添加切面方法，关于切面方法：</p>
<ol>
<li>
<p>应该使用<code>public</code>权限；</p>
</li>
<li>
<p>可以使用<code>void</code>作为返回值类型，如果是<code>@Around</code>必须使用<code>Object</code>作为返回值类型；</p>
</li>
<li>
<p>方法名称可以自由定义；</p>
</li>
<li>
<p>参数根据切面的作用范围来决定，例如使用<code>@Around</code>时必须添加<code>ProceedingJoinPoint</code>参数。</p>
</li>
</ol>
<p>所以，在切面类中添加切面方法：</p>
<pre><code>public void process(ProceedingJoinPoint pjp) throws Throwable {
    // 记录起始时间
    long start = System.currentTimeMillis();

    // 执行任务
    pjp.proceed();

    // 记录结束时间
    long end = System.currentTimeMillis();

    // 统计耗时
    System.err.println(&quot;耗时：&quot; + (end - start) + &quot;ms.&quot;);
}
</code></pre>

<p>最后，还应该确定该切面方法的作用范围，即何时、哪些处理流程中，需要应用这个切面！</p>
<p>根据作用范围，可以使用<code>@Before</code>、<code>@After</code>，或使用<code>@Around</code>表示在应用的方法之前和之后都需要执行某些任务。</p>
<p>然后，还需要在<code>@Around</code>注解中配置参数，以决定切面应用于哪里，例如：</p>
<pre><code>@Around(&quot;execution(* cn.tedu.store.service.impl.*.*(..))&quot;)
</code></pre>

<p>以上表达式表示将切面应用于<code>cn.tedu.store.service.impl</code>包下所有的类中的所有方法！</p>
<p>至此，对于应用范围之内所有返回值类型为<code>void</code>的业务方法都可以正常应用切面，如果需要保证其它业务方法也能正常运行，则需要获取方法执行时的返回值，例如：</p>
<pre><code>// 执行任务
Object obj = pjp.proceed();
</code></pre>

<p>并且，整个切面方法也需要将返回值类型设置为<code>Object</code>，并在方法运行结束之前返回以上结果。</p>
<p>使用<code>@Before</code>表示切面将存于某位置之前，使用<code>@After</code>表示切面将存在于某位置之后，通常，使用<code>@Around</code>可以完全取代<code>@Before</code>或<code>@After</code>！</p>
<pre><code>注册：请求 -----&gt; Controller -----&gt; Service -----&gt; Mapper

登录：请求 -----&gt; Controller -----&gt; Service -----&gt; Mapper

头像：请求 -----&gt; Controller -----&gt; Service -----&gt; Mapper

资料：请求 -----&gt; Controller -----&gt; Service -----&gt; Mapper

密码：请求 -----&gt; Controller -----&gt; Service -----&gt; Mapper
</code></pre>


</body>
</html>
<!-- This document was created with MarkdownPad, the Markdown editor for Windows (http://markdownpad.com) -->
